package com.atguigu.tingshu.user.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.cache.GuiGuCache;
import com.atguigu.tingshu.common.constant.KafkaConstant;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.service.KafkaService;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.user.*;
import com.atguigu.tingshu.user.mapper.*;
import com.atguigu.tingshu.user.pattern.UserPaidStrategy;
import com.atguigu.tingshu.user.pattern.facotry.StrategyFactory;
import com.atguigu.tingshu.user.service.UserInfoService;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {

    @Autowired
    private UserInfoMapper userInfoMapper;


    @Autowired
    private WxMaService wxMaService;


    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private KafkaService kafkaService;


    /**
     * 微信小程序登录，将微信账户openId跟本地用户关联，返回自定义登陆态(token令牌)
     *
     * @param code 小程序端提交临时凭据，用于服务器端获取微信openId
     * @return {"token":"UUID令牌"}
     */
    @Override
    public Map<String, String> wxLogin(String code) {
        try {
            //1.获取当前登录微信账户唯一标识：wxOpenId
            WxMaJscode2SessionResult sessionInfo = wxMaService.getUserService().getSessionInfo(code);
            String openid = sessionInfo.getOpenid();

            //2.判断当前微信是否为初次登录小程序
            LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(UserInfo::getWxOpenId, openid);
            UserInfo userInfo = userInfoMapper.selectOne(queryWrapper);

            //3.初次登录，初始化用户记录以及账户记录（余额）
            if (userInfo == null) {
                //3.1 保存用户记录-将微信用户openID关联自定义用户
                userInfo = new UserInfo();
                userInfo.setWxOpenId(openid);
                userInfo.setNickname("听友" + IdUtil.getSnowflakeNextId());
                userInfo.setAvatarUrl("https://oss.aliyuncs.com/aliyun_id_photo_bucket/default_handsome.jpg");
                userInfo.setIsVip(0);
                userInfo.setVipExpireTime(null);
                userInfoMapper.insert(userInfo);
                //3.2 发送Kafka消息 消息：用户ID
                kafkaService.sendKafkaMessage(KafkaConstant.QUEUE_USER_REGISTER, userInfo.getId().toString());
            }
            //4.产生登录令牌（自定义登陆态）基于用户信息跟token进行关联
            //4.1 生成自定义token令牌
            String token = IdUtil.randomUUID();
            //4.2 构建登录key 形式：user:login:token
            String loginKey = RedisConstant.USER_LOGIN_KEY_PREFIX + token;
            //4.3 将token作为Key 将用户信息UserInfoVo作为Value写入Redis
            UserInfoVo userInfoVo = BeanUtil.copyProperties(userInfo, UserInfoVo.class);
            redisTemplate.opsForValue().set(loginKey, userInfoVo, RedisConstant.USER_LOGIN_KEY_TIMEOUT, TimeUnit.SECONDS);

            //5.封装登录结果
            Map<String, String> map = new HashMap<>();
            map.put("token", token);
            return map;
        } catch (Exception e) {
            log.error("[用户服务]微信登录异常：{}", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取当前登录用户基本信息
     *
     * @param userId
     * @return
     */
    @Override
    @GuiGuCache(prefix = "user:")
    public UserInfoVo getUserInfo(Long userId) {
        UserInfo userInfo = userInfoMapper.selectById(userId);
        Assert.notNull(userInfo, "当前用户不存在！");
        return BeanUtil.copyProperties(userInfo, UserInfoVo.class);
    }

    /**
     * 更新当前用户基本信息
     *
     * @param userInfoVo
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUser(UserInfoVo userInfoVo) {
        //一、采用延时双删策略确保一致
        //1.获取当前登录用户ID
        Long userId = AuthContextHolder.getUserId();
        //  先删除一次缓存
        redisTemplate.delete("user:" + userId);

        //2.只允许用户修改昵称、头像其他信息不支持修改
        UserInfo userInfo = new UserInfo();
        userInfo.setId(userId);
        userInfo.setNickname(userInfoVo.getNickname());
        userInfo.setAvatarUrl(userInfoVo.getAvatarUrl());
        userInfoMapper.updateById(userInfo);

        //   睡眠一段时间（确保并发读操作将“脏数据”放入缓存） 时间大于读线程耗时
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        //   再次删除一次缓存
        redisTemplate.delete("user:" + userId);
    }


    @Autowired
    private UserPaidAlbumMapper userPaidAlbumMapper;

    @Autowired
    private UserPaidTrackMapper userPaidTrackMapper;

    /**
     * 提供给专辑服务动态判断声音付费标识业务接口，其中请求体参数声音ID集合，每一页中声音ID都需要提交来进行验证购买情况
     *
     * @param userId               用户ID
     * @param albumId              专辑ID
     * @param needCheckTrackIdList 待检查购买情况声音ID集合
     * @return {专辑ID:购买结果}   1：已购   0：未购买
     */
    @Override
    public Map<Long, Integer> userIsPaidTrack(Long userId, Long albumId, List<Long> needCheckTrackIdList) {
        Map<Long, Integer> map = new HashMap<>();
        //1.优先根据专辑ID判断是否购买过专辑，如果已购专辑，待检查声音ID购买情况全部设置1返回
        LambdaQueryWrapper<UserPaidAlbum> userPaidAlbumLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userPaidAlbumLambdaQueryWrapper.eq(UserPaidAlbum::getUserId, userId);
        userPaidAlbumLambdaQueryWrapper.eq(UserPaidAlbum::getAlbumId, albumId);
        Long count = userPaidAlbumMapper.selectCount(userPaidAlbumLambdaQueryWrapper);
        if (count > 0) {
            for (Long paidTrackId : needCheckTrackIdList) {
                map.put(paidTrackId, 1);
            }
            return map;
        }
        //2.根据专辑ID+用户ID查询已购声音表记录，得到已购声音ID集合
        LambdaQueryWrapper<UserPaidTrack> userPaidTrackLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userPaidTrackLambdaQueryWrapper.eq(UserPaidTrack::getUserId, userId);
        userPaidTrackLambdaQueryWrapper.eq(UserPaidTrack::getAlbumId, albumId);
        userPaidTrackLambdaQueryWrapper.select(UserPaidTrack::getTrackId);
        List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(userPaidTrackLambdaQueryWrapper);
        //3.如果已购声音ID为空，将待检查声音ID购买情况全部设置为0返回
        if (CollectionUtil.isEmpty(userPaidTrackList)) {
            //说明该用户未购买任意声音
            for (Long notPayTrackId : needCheckTrackIdList) {
                map.put(notPayTrackId, 0);
            }
            return map;
        }
        //4.存在已购买声音，找出已购买过声音ID将已购声音购买情况全部设置1 反之设置为0
        //4.1 从已购声音集合中获取已购声音ID集合
        List<Long> userPaidTrackIdList = userPaidTrackList.stream().map(UserPaidTrack::getTrackId).collect(Collectors.toList());
        //4.2 循环待检测购买情况声音ID集合 如果待检测声音ID包含在已购声音ID集合中 手动将购买情况设置1，反之设置为0
        for (Long needCheckPayStausTrackId : needCheckTrackIdList) {
            if (userPaidTrackIdList.contains(needCheckPayStausTrackId)) {
                //说明该声音已购买
                map.put(needCheckPayStausTrackId, 1);
            } else {
                map.put(needCheckPayStausTrackId, 0);
            }
        }
        return map;
    }

    /**
     * 根据当前登录用户ID+专辑ID查询是否存在购买记录
     *
     * @param albumId
     * @return
     */
    @Override
    public Boolean isPaidAlbum(Long albumId) {
        //1.获取当前用户ID
        Long userId = AuthContextHolder.getUserId();
        //2.用户ID+专辑ID查询是否存在购买记录
        LambdaQueryWrapper<UserPaidAlbum> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserPaidAlbum::getUserId, userId);
        queryWrapper.eq(UserPaidAlbum::getAlbumId, albumId);
        Long count = userPaidAlbumMapper.selectCount(queryWrapper);
        return count > 0;
    }

    /**
     * 查询当前用户某个专辑中已购声音id集合
     *
     * @param albumId 专辑ID
     * @return
     */
    @Override
    public List<Long> getUserPaidTrackIdList(Long albumId) {
        //1.获取当前用户ID
        Long userId = AuthContextHolder.getUserId();
        //2.根据用户ID+专辑ID查询已购声音列表
        LambdaQueryWrapper<UserPaidTrack> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserPaidTrack::getUserId, userId);
        queryWrapper.eq(UserPaidTrack::getAlbumId, albumId);
        queryWrapper.select(UserPaidTrack::getTrackId);
        List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(queryWrapper);
        //3.获取已购声音ID列表
        if (CollectionUtil.isNotEmpty(userPaidTrackList)) {
            List<Long> userPaidTrackIdList = userPaidTrackList.stream()
                    .map(UserPaidTrack::getTrackId)
                    .collect(Collectors.toList());
            return userPaidTrackIdList;
        }
        return null;
    }


    @Autowired
    private AlbumFeignClient albumFeignClient;

    @Autowired
    private UserVipServiceMapper userVipServiceMapper;

    @Autowired
    private VipServiceConfigMapper vipServiceConfigMapper;

    @Autowired
    private StrategyFactory strategyFactory;


    /**
     * 提交订单支付（余额，支付宝）成功-虚拟物品发货
     *
     * @param userPaidRecordVo
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void savePaidRecord(UserPaidRecordVo userPaidRecordVo) {
        //付款项目类型: 1001-专辑 1002-声音 1003-vip会员
        //1.根据购买项目类型获取具体策略实现类对象
        UserPaidStrategy userPaidStrategy = strategyFactory.getStrategy(userPaidRecordVo.getItemType());
        //2.调用不同策略实现类实现不同行为
        userPaidStrategy.saveUserPaidRecord(userPaidRecordVo);
    }

    /**
     * 更新VIP状态：处理过期会员
     *
     * @return
     */
    @Override
    public void updateVipExpireStatus(Date now) {
        LambdaUpdateWrapper<UserInfo> updateWrapper = new LambdaUpdateWrapper<>();
        //更新条件：会员过期时间《当前时间
        updateWrapper.lt(UserInfo::getVipExpireTime, now);
        //更新字段
        updateWrapper.set(UserInfo::getIsVip, 0);
        this.update(updateWrapper);
    }

}
